using System;
using System.Runtime.InteropServices;
using System.Reflection;
using System.Collections;
using System.Text;
using BassWrapper;

namespace MusicPlayer
{

	[ComImport, 
	InterfaceType(ComInterfaceType.InterfaceIsIUnknown), 
	Guid("B196B283-BAB4-101A-B69C-00AA00341D07")]
	public interface IProvideClassInfo
	{
		[PreserveSig()]
		UInt32 GetClassInfo(out IntPtr ppTI);
	}

	[
	Guid("62B8D26E-6DA8-4e5f-BEB8-B5B8C242491C"),
	InterfaceType(ComInterfaceType.InterfaceIsIDispatch)
	]
	public interface _Player 
	{

		string Filter 
		{
			get;
		}

		int PlayMusic(string filename);

		void Stop();

		double PercentageCompletedProp
		{
			get; set;
		}
	}

	[
	Guid("5A166F34-DB39-4845-A8F5-2C6664EB983C"),
	InterfaceType(ComInterfaceType.InterfaceIsIDispatch)
	]
	public interface _PlayerEvents
	{
		void PercentageCompleted(double percentage);
	}

	[ComVisible(false)]
	public delegate void PercentageCompletedDelegate(double percentage);

	[
	Guid("BE56E5B7-3D98-4c6f-8097-3FD898B1E1B7"),
	ClassInterface(ClassInterfaceType.None),
	ComSourceInterfaces(typeof(_PlayerEvents)),
	ProgId("MusicPlayer")
	]
	public class Player : _Player, IProvideClassInfo
	{

		private int current = 0;
		private IList plugins = null;
		private SecureTimer timer = null;
		private int updateInterval = 50; // 50ms
		private int deviceLatencyBytes = 0;
		private int deviceLatencyMS = 0;
		private long totalLengthBytes = 0;

		private double percentageCompleted = 0.0;

		public event PercentageCompletedDelegate PercentageCompleted;

		private void InitPlugins() 
		{
			//System.IO.DirectoryInfo dirInfo = new System.IO.DirectoryInfo(@"C:\BASS");
			foreach (string file in System.IO.Directory.GetFiles(@"C:\BASS", "bass*.dll"))
			{
				if (file.EndsWith("bass.dll")) // skip bass.dll
				{
					continue;
				}

				int plugin = Bass.BASS_PluginLoad(file,0);
				if (plugin == 0)
				{
					System.Windows.Forms.MessageBox.Show(string.Format("load {0}: {1}" + "\n", file, plugin));
					int error = Bass.BASS_ErrorGetCode();
					System.Windows.Forms.MessageBox.Show( Enum.GetName(typeof(BassWrapper.BASSErrorCode), error) );
				} 
				else 
				{
					plugins.Add(plugin);
				}
			}
		}

		public Player() 
		{
			plugins = new ArrayList();
			timer = new SecureTimer(updateInterval);
			timer.Tick += new EventHandler(timerUpdate_Tick);
			this.PercentageCompleted += new PercentageCompletedDelegate(Player_PercentageCompleted);

			try 
			{
				//if ( Bass.BASS_Init(-1, 44100, BASSInit.BASS_DEVICE_DEFAULT | BASSInit.BASS_DEVICE_LATENCY, this.Handle.ToInt32(), null) )
				if (Bass.BASS_Init(-1, 48000, 2, IntPtr.Zero, null) )
				{
					BASS_INFO info = new BASS_INFO();
					Bass.BASS_GetInfo( info );
					deviceLatencyMS = info.latency;
				}
				else
					System.Windows.Forms.MessageBox.Show("Bass_Init error!" );
			} 
			catch (Exception e) 
			{
				System.Windows.Forms.MessageBox.Show(e.Message);
			}

			InitPlugins();
		}

		public int PlayMusic(string filename) 
		{
			timer.Stop();

			// create the stream
			current = Bass.BASS_StreamCreateFile(false, filename, 0, 0, 
				(int) (BassWrapper.BASSStream.BASS_SAMPLE_FLOAT | BassWrapper.BASSStream.BASS_DEFAULT | BassWrapper.BASSStream.BASS_UNICODE));
			if (current != 0)
			{
				// latency from milliseconds to bytes
				deviceLatencyBytes = (int)Bass.BASS_ChannelSeconds2Bytes(current, deviceLatencyMS/1000f);
				totalLengthBytes = Bass.BASS_ChannelGetLength(current, 0); // length in bytes
			}
			bool play = Bass.BASS_ChannelPlay(current, false);

			if (current != 0 && play)
			{
				percentageCompleted = 0.0;
				timer.Start();
			}
			else
			{
				int error = Bass.BASS_ErrorGetCode();
				System.Windows.Forms.MessageBox.Show( Enum.GetName(typeof(BassWrapper.BASSErrorCode), error) );
			}

			return current;
		}

		
		/// <summary>
		/// Clean up any resources being used.
		/// </summary>
		~Player()
		{
			Stop();
			timer.Tick -= new EventHandler(timerUpdate_Tick);

			Bass.BASS_PluginFree(0);
			// close bass
			Bass.BASS_Stop();
			Bass.BASS_Free();
		}

		public void Stop() 
		{
			timer.Stop();
			if (current != 0) 
			{
				Bass.BASS_StreamFree(current);
				current = 0;
				totalLengthBytes = 0;
				percentageCompleted = 0.0;
			}
		}

		public string Filter 
		{
			get 
			{
				StringBuilder sb = new StringBuilder();
				foreach (string s in Bass.BASSAddOnGetPluginFileExt(plugins, true)) 
				{
					sb.Append(s + "\n");
				}
				
				return sb.ToString();
			}
		}

		private void timerUpdate_Tick(object sender, System.EventArgs e) 
		{
			lock (this) 
			{
				// here we gather info about the stream, when it is playing...
				if ( Bass.BASS_ChannelIsActive(current) == (int)BASSActive.BASS_ACTIVE_PLAYING )
				{
					// the stream is still playing...
				}
				else
				{
					// the stream is NOT playing anymore...
					timer.Stop();
					Stop();
					return;
				}

				long pos = Bass.BASS_ChannelGetPosition(current, 0); // position in bytes

				if (totalLengthBytes - pos != deviceLatencyBytes) 
				{
					pos -= deviceLatencyBytes;
				}
				//System.Windows.Forms.MessageBox.Show((pos / (double) len) + "");
				try 
				{
					this.PercentageCompleted(pos / (double) totalLengthBytes);
				} 
				catch (Exception exception) 
				{
					System.Windows.Forms.MessageBox.Show(exception.InnerException.Message);
				}
			}
		}

		private void Player_PercentageCompleted(double percentage)
		{
			percentageCompleted = percentage;
		}

		public double PercentageCompletedProp 
		{
			get 
			{
				return percentageCompleted;
			}
			set 
			{
				lock (this) 
				{
					percentageCompleted = value;
					long pos = (long) (totalLengthBytes * value);
					Bass.BASS_ChannelSetPosition(current, pos, 0);
				}
			}
		}

		#region IProvideClassInfo Members

		public UInt32 GetClassInfo(out IntPtr ppTI) 
		{
			ppTI = Marshal.GetITypeInfoForType(GetType());
			return 0x00000000;
		}

		#endregion
	}
}
